﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using Sinacor.OMM.Util;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Sinacor.OMM.Logging;
using PSE.Framework.Messaging;

namespace Sinacor.OMM.ConnectionManager.InternalQueue
{
    public class InternalQueueManager<T>
    {
        private static InternalQueueManager<T> _queueManager;
        private static Mutex _mutex = new Mutex();
        private Dictionary<string, SynchronizedQueue<T>> _managedQueuesDict;

        public delegate void MessageArrival(T item, string queueId);
        public delegate void MessageArrivalList(List<T> listItens, string queueId);

        public delegate void HealthStatusChanged(string queueId, SynchronizedQueue<T> managedQueue);
        public event HealthStatusChanged OnHealthStatusChanged;


        private InternalQueueManager()
        {
            _managedQueuesDict = new Dictionary<string, SynchronizedQueue<T>>();
        }

        public static InternalQueueManager<T> GetInstance()
        {
            _mutex.WaitOne();

            if (_queueManager == null)
                _queueManager = new InternalQueueManager<T>();

            _mutex.ReleaseMutex();

            return _queueManager;
        }

        public void Dispose()
        {
            lock (_managedQueuesDict)
            {
                lock (_queueManager.ManagedQueues)
                {
                    foreach (SynchronizedQueue<T> managedQueue in _managedQueuesDict.Values)
                    {
                        if (managedQueue != null)
                            managedQueue.Dispose();
                    }
                }
                _managedQueuesDict.Clear();
            }
        }

        #region Métodos de Manipulação de fila
        public void Enqueue(string queueId, T item)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].Enqueue(item);
                    }
                }
                else
                {
                    _managedQueuesDict.Add(queueId, new SynchronizedQueue<T>(queueId, QueueManager_OnManagedQueueHealthStatus));
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].Enqueue(item);
                    }
                }
            }
        }

        public T Dequeue(string queueId)
        {
            T ret;

            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                        ret = _managedQueuesDict[queueId].Dequeue();
                }
                else
                {
                    ret = default(T);
                }
            }

            return ret;
        }

        public List<T> DequeueBlock(string queueId)
        {
            List<T> listQueuedItens = new List<T>();

            lock (_managedQueuesDict[queueId])
            {
                if (_managedQueuesDict[queueId].Count > 0)
                {
                    while (_managedQueuesDict[queueId].Count > 0)
                        listQueuedItens.Add(_managedQueuesDict[queueId].Dequeue());
                }
            }

            return listQueuedItens;
        }

        public List<T> DequeueBlock(string queueId, int maxItens)
        {
            List<T> listQueuedItens = new List<T>();
            int cont = 0;

            lock (_managedQueuesDict[queueId])
            {
                if (_managedQueuesDict[queueId].Count > 0)
                {
                    while (_managedQueuesDict[queueId].Count > 0 || cont <= maxItens)
                    {
                        listQueuedItens.Add(_managedQueuesDict[queueId].Dequeue());
                        cont++;
                    }
                }
            }

            return listQueuedItens;
        }

        public T Peek(string queueId)
        {
            T ret;

            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                        ret = _managedQueuesDict[queueId].Peek();
                }
                else
                {
                    ret = default(T);
                }
            }
            return ret;
        }

        public long Count(string queueId)
        {
            long ret;

            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        ret = _managedQueuesDict[queueId].Count;
                    }
                }
                else
                    ret = -1;
            }

            return ret;
        }

        public void Clear(string queueId)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].Clear();
                    }
                }
            }
        }

        public void ClearAllQueues()
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.Count > 0)
                {
                    foreach (SynchronizedQueue<T> managedQueue in _managedQueuesDict.Values)
                    {
                        lock (managedQueue)
                            managedQueue.Clear();
                    }
                }
            }
        }
        #endregion

        public Dictionary<string, SynchronizedQueue<T>>.KeyCollection Keys
        {
            get
            {
                return _managedQueuesDict.Keys;
            }
        }

        public Dictionary<string, SynchronizedQueue<T>>.ValueCollection Queues
        {
            get
            {
                return _managedQueuesDict.Values;
            }
        }

        public Dictionary<string, SynchronizedQueue<T>> ManagedQueues
        {
            get
            {
                return _managedQueuesDict;
            }
        }

        #region Método para definir detalhes do receiver
        public void SetMaxItensInBlockedEvent(string queueId, int maxItens)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                        _managedQueuesDict[queueId].MaxItemsBlock = maxItens;
                }
                else
                {
                    _managedQueuesDict.Add(queueId, new SynchronizedQueue<T>(queueId, QueueManager_OnManagedQueueHealthStatus));
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].MaxItemsBlock = maxItens;
                    }
                }
            }
        }

        public int GetMaxItensInBlockedEvent(string queueId)
        {
            int ret;

            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                        ret = _managedQueuesDict[queueId].MaxItemsBlock;
                }
                else
                {
                    _managedQueuesDict.Add(queueId, new SynchronizedQueue<T>(queueId, QueueManager_OnManagedQueueHealthStatus));
                    lock (_managedQueuesDict[queueId])
                    {
                        ret = _managedQueuesDict[queueId].MaxItemsBlock;
                    }
                }
            }

            return ret;
        }

        public void SetMessageArrival(string queueId, SynchronizedQueue<T>.SynchronizedQueueMessageHandler messageArrival)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        if (messageArrival != null)
                        {
                            //if (!_managedQueuesDict[queueId].MessageArrivalSubscribed)
                            //    _managedQueuesDict[queueId].OnManagedQueueMessageArrivalEvent += new InternalQueueManager<T>.ManagedQueue<T>.ManagedQueueMessageHandler(QueueManager_OnManagedQueueMessageArrivalEvent);

                            _managedQueuesDict[queueId].MessageArrival = messageArrival;
                            _managedQueuesDict[queueId].MessageArrivalType = MessageArrivalType.NonBlock;
                        }
                    }
                }
                else
                {
                    _managedQueuesDict.Add(queueId, new SynchronizedQueue<T>(queueId, QueueManager_OnManagedQueueHealthStatus));
                    lock (_managedQueuesDict[queueId])
                    {
                        //_managedQueuesDict[queueId].OnManagedQueueMessageArrivalEvent += new InternalQueueManager<T>.ManagedQueue<T>.ManagedQueueMessageHandler(QueueManager_OnManagedQueueMessageArrivalEvent);
                        _managedQueuesDict[queueId].MessageArrival = messageArrival;
                        _managedQueuesDict[queueId].MessageArrivalType = MessageArrivalType.NonBlock;
                    }
                }
            }
        }

        public void SetMessageArrival(string queueId, SynchronizedQueue<T>.SynchronizedQueueMessageListHandler messageArrivalList)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        //                        if (!_managedQueuesDict[queueId].MessageListArrivalSubscribed)
                        //                            _managedQueuesDict[queueId].OnManagedQueueMessageListArrivalEvent += new InternalQueueManager<T>.ManagedQueue<T>.ManagedQueueMessageListHandler(QueueManager_OnManagedQueueMessageListArrivalEvent);

                        _managedQueuesDict[queueId].MessageArrivalList = messageArrivalList;
                        _managedQueuesDict[queueId].MessageArrivalType = MessageArrivalType.Block;
                    }
                }
                else
                {
                    _managedQueuesDict.Add(queueId, new SynchronizedQueue<T>(queueId, QueueManager_OnManagedQueueHealthStatus));
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].MessageArrivalList = messageArrivalList;
                        //                        _managedQueuesDict[queueId].OnManagedQueueMessageListArrivalEvent += new InternalQueueManager<T>.ManagedQueue<T>.ManagedQueueMessageListHandler(QueueManager_OnManagedQueueMessageListArrivalEvent);
                        _managedQueuesDict[queueId].MessageArrivalType = MessageArrivalType.Block;
                    }
                }
            }
        }

        public void PauseReceiver(string queueId)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].Pause();
                    }
                }
            }
        }

        public void ResumeReceiver(string queueId)
        {
            lock (_managedQueuesDict)
            {
                if (_managedQueuesDict.ContainsKey(queueId))
                {
                    lock (_managedQueuesDict[queueId])
                    {
                        _managedQueuesDict[queueId].Resume();
                    }
                }
            }
        }

        #endregion

        #region Eventos para entrega de mensagens no receiver
        private void QueueManager_OnManagedQueueMessageArrivalEvent(SynchronizedQueue<T> sender, object item, string queueId)
        {
            try
            {
                sender.MessageArrival(sender, (T)item, queueId);
            }
            catch (Exception ex)
            {
                //TODO: Log
                throw ex;
            }
        }

        private void QueueManager_OnManagedQueueMessageListArrivalEvent(SynchronizedQueue<T> sender, object item, string queueId)
        {
            try
            {
                List<object> receivedList = (List<object>)item;
                List<T> newConvertedList = new List<T>();
                foreach (object i in receivedList)
                    newConvertedList.Add((T)i);

                sender.MessageArrivalList(sender, newConvertedList, queueId);
            }
            catch (Exception ex)
            {
                //TODO: Log
                throw ex;
            }
        }

        private void QueueManager_OnManagedQueueHealthStatus(string queueId, SynchronizedQueue<T> managedQueue)
        {
            try
            {
                if (OnHealthStatusChanged != null)
                {
                    OnHealthStatusChanged(queueId, managedQueue);
                }
            }
            catch (Exception ex)
            {
                //TODO: Log
            }
        }
        #endregion
        
    }
}
